home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / shar / shar.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  9KB  |  316 lines

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <ctype.h>
  5.  
  6. /*{
  7. Shar puts readable text files together in a package
  8. from which they are easy to extract.  The original version
  9. was a shell script posted to the net, shown below:
  10.     #Date: Mon Oct 18 11:08:34 1982
  11.     #From: decvax!microsof!uw-beave!jim (James Gosling at CMU)
  12.     AR=$1
  13.     shift
  14.     for i do
  15.         echo a - $i
  16.         echo "echo x - $i" >>$AR
  17.         echo "cat >$i <<'!Funky!Stuff!'" >>$AR
  18.         cat $i >>$AR
  19.         echo "!Funky!Stuff!" >>$AR
  20.     done
  21. I rewrote this version in C to provide better diagnostics
  22. and to run faster.  The major difference is that my version
  23. does not affect any files because it prints to the standard
  24. output.  Mine also has several options.
  25.  
  26. Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731
  27.  
  28. Many enhancements motivated by Michael Thompson.
  29.  
  30. Directory archiving motivated by Derek Zahn @ wisconsin
  31.     His version had some problems, so I wrote a general
  32.     routine for traversing a directory hierarchy.  It
  33.     allows marching through a directory on old and new
  34.     UNIX systems.
  35. }*/
  36.  
  37. /* COMMANDS */
  38. #define    EXTRACT "#! /bin/sh"     /* magic exec string at shar file start */
  39. #define    PATH    "/bin:$PATH"     /* search path for programs */
  40. #define    CAT     "cat";           /* /bin/cat */
  41. #define    SED     "sed 's/^%s//'"  /* /bin/sed removes Prefix from lines */
  42. #define    MKDIR   "mkdir"          /* make a new dirctory */
  43. #define    CHMOD   "chmod +x"       /* change file protection (for executables) */
  44. #define    CHDIR   "cd"             /* change current directory */
  45. #define    TEST    "test"           /* /bin/test files */
  46. #define    WC_C    "wc -c <"        /* counts chars in file */
  47. #define    ECHO    "echo shar"      /* echo a message to extractor */
  48.  
  49. main (argc, argv) char **argv;    
  50.     {
  51.     int     shar ();
  52.     int     optind;
  53.     if ((optind = initial (argc, argv)) < 0)
  54.         exit (1);
  55.     if (header (argc, argv, optind))
  56.         exit (2);
  57.     while (optind < argc)
  58.         traverse (argv[optind++], shar);
  59.     footer ();
  60.     exit (0);
  61.     }
  62.  
  63. /*            OPTIONS            */
  64. typedef    int    lgl;
  65. #define    true    ((lgl) 1)
  66. #define    false    ((lgl) 0)
  67. int     Lastchar;   /* the last character printed */
  68. int     Ctrlcount;  /* how many bad control characters are in file */
  69.  
  70. #define    USAGE "[-abcsv] [-p prefix] [-d delim] files > archive"
  71. #define    OPTSTRING "abcsvp:d:"
  72.  
  73. lgl     Verbose = false;       /* provide append/extract feedback */
  74. lgl     Basename = false;      /* extract into basenames */
  75. lgl     Count = false;         /* count characters to check transfer */
  76. lgl     Silent = false;        /* turn off all verbosity */
  77. char    *Delim = "SHAR_EOF";   /* put after each file */
  78. char    Filter[100] = CAT;     /* used to extract archived files */
  79. char    *Prefix = NULL;        /* line prefix to avoid funny chars */
  80.  
  81. int /* returns the index of the first operand file */
  82. initial (argc, argv) char **argv;
  83.     {
  84.     int     errflg = 0;
  85.     extern    int     optind;
  86.     extern    char    *optarg;
  87.     int     C;
  88.     while ((C = getopt (argc, argv, OPTSTRING)) != EOF)
  89.         switch (C)
  90.             {
  91.             case 'v': Verbose = true; break;
  92.             case 'c': Count = true; break;
  93.             case 'b': Basename = true; break;
  94.             case 'd': Delim = optarg; break;
  95.             case 's': /* silent running */
  96.                 Silent = true;
  97.                 Verbose = false;
  98.                 Count = false;
  99.                 Prefix = NULL;
  100.                 break;
  101.             case 'a': /* all the options */
  102.                 Verbose = true;
  103.                 Count = true;
  104.                 Basename = true;
  105.                 /* fall through to set prefix */
  106.                 optarg = "    X";
  107.             case 'p': (void) sprintf (Filter, SED, Prefix = optarg); break;
  108.             default: errflg++;
  109.             }
  110.     if (errflg || optind == argc)
  111.         {
  112.         if (optind == argc)
  113.             fprintf (stderr, "shar: No input files\n");
  114.         fprintf (stderr, "USAGE: shar %s\n", USAGE);
  115.         return (-1);
  116.         }
  117.     return (optind);
  118.     }
  119.  
  120. header (argc, argv, optind)
  121. char    **argv;
  122.     {
  123.     int     i;
  124.     lgl     problems = false;
  125.     long    clock;
  126.     char    *ctime ();
  127.     char    *getenv ();
  128.     char    *NAME = getenv ("NAME");
  129.     char    *ORG = getenv ("ORGANIZATION");
  130.     for (i = optind; i < argc; i++)
  131.         if (access (argv[i], 4)) /* check read permission */
  132.             {
  133.             fprintf (stderr, "shar: Can't read '%s'\n", argv[i]);
  134.             problems++;
  135.             }
  136.     if (problems) return (problems);
  137.     /*    I have given up on putting a cut line in the archive.
  138.         Too many people complained about having to remove it.
  139.         puts ("-----cut here-----cut here-----cut here-----cut here-----");
  140.     */
  141.     puts (EXTRACT);
  142.     puts ("# This is a shell archive, meaning:");
  143.     printf ("# 1. Remove everything above the %s line.\n", EXTRACT);
  144.     puts ("# 2. Save the resulting text in a file.");
  145.     puts ("# 3. Execute the file with /bin/sh (not csh) to create the files:");
  146.     for (i = optind; i < argc; i++)
  147.         printf ("#\t%s\n", argv[i]);
  148.     (void) time (&clock);
  149.     printf ("# This archive created: %s", ctime (&clock));
  150.     if (NAME)
  151.         printf ("# By:\t%s (%s)\n", NAME, ORG ? ORG : "");
  152.     printf ("export PATH; PATH=%s\n", PATH);
  153.     return (0);
  154.     }
  155.  
  156. footer ()
  157.     {
  158.     puts ("#\tEnd of shell archive");
  159.     puts ("exit 0");
  160.     }
  161.  
  162. archive (input, output)
  163. char    *input, *output;
  164.     {
  165.     char    buf[BUFSIZ];
  166.     FILE    *ioptr;
  167.     if (ioptr = fopen (input, "r"))
  168.         {
  169.         if (Count == true)
  170.             {
  171.             Ctrlcount = 0;    /* no bad control characters so far */
  172.             Lastchar = '\n';  /* simulate line start */
  173.             }
  174.         printf ("%s << \\%s > '%s'\n", Filter, Delim, output);
  175.         if (Prefix)
  176.             {
  177.             while (fgets (buf, BUFSIZ, ioptr))
  178.                 {
  179.                 if (Prefix) outline (Prefix);
  180.                 outline (buf);
  181.                 }
  182.             }
  183.         else copyout (ioptr);
  184.         /* thanks to H. Morrow Long (ittvax!long) for the next fix */
  185.         if (Lastchar != '\n') /* incomplete last line */
  186.             putchar ('\n');   /* Delim MUST begin new line! */
  187.         puts (Delim);
  188.         if (Count == true && Lastchar != '\n')
  189.             printf ("%s: a missing newline was added to \"'%s'\"\n", ECHO, input);
  190.         if (Count == true && Ctrlcount)
  191.             printf ("%s: %d control character%s may be missing from \"'%s'\"\n",
  192.                 ECHO, Ctrlcount, Ctrlcount > 1 ? "s" : "", input);
  193.         (void) fclose (ioptr);
  194.         return (0);
  195.         }
  196.     else
  197.         {
  198.         fprintf (stderr, "shar: Can't open '%s'\n", input);
  199.         return (1);
  200.         }
  201.     }
  202.  
  203. /*
  204.     Copyout copies its ioptr almost as fast as possible
  205.     except that it has to keep track of the last character
  206.     printed.  If the last character is not a newline, then
  207.     shar has to add one so that the end of file delimiter
  208.     is recognized by the shell.  This checking costs about
  209.     a 10% difference in user time.  Otherwise, it is about
  210.     as fast as cat.  It also might count control characters.
  211. */
  212. #define    badctrl(c) (iscntrl (c) && !isspace (c))
  213. copyout (ioptr)
  214. register    FILE    *ioptr;
  215.     {
  216.     register    int     C;
  217.     register    int     L;
  218.     register    count;
  219.     count = Count;
  220.     while ((C = getc (ioptr)) != EOF)
  221.         {
  222.         if (count == true && badctrl (C)) Ctrlcount++;
  223.         L = putchar (C);
  224.         }
  225.     Lastchar = L;
  226.     }
  227.  
  228. outline (s)
  229. register    char    *s;
  230.     {
  231.     if (*s)
  232.         {
  233.         while (*s)
  234.             {
  235.             if (Count == true && badctrl (*s)) Ctrlcount++;
  236.             putchar (*s++);
  237.             }
  238.         Lastchar = *(s-1);
  239.         }
  240.     }
  241.  
  242. #define    FSIZE     statbuf.st_size
  243. shar (file, type, pos)
  244. char    *file;     /* file or directory to be processed */
  245. int     type;      /* either 'f' for file or 'd' for directory */
  246. int     pos;       /* 0 going in to a file or dir, 1 going out */
  247.     {
  248.     struct    stat    statbuf;
  249.     char    *basefile = file;
  250.     if (!strcmp (file, ".")) return;
  251.     if (stat (file, &statbuf)) FSIZE = 0;
  252.     if (Basename == true)
  253.         {
  254.         while (*basefile) basefile++; /* go to end of name */
  255.         while (basefile > file && *(basefile-1) != '/') basefile--;
  256.         }
  257.     if (pos == 0) /* before the file starts */
  258.         {
  259.         if (type == 'd')
  260.             {
  261.             printf ("if %s ! -d '%s'\n", TEST, basefile);
  262.             printf ("then\n");
  263.             if (Verbose == true)
  264.                 printf ("    %s: creating directory \"'%s'\"\n", ECHO, basefile);
  265.             printf ("    %s '%s'\n", MKDIR, basefile);
  266.             printf ("fi\n");
  267.             if (Verbose == true)
  268.                 printf ("%s: entering directory \"'%s'\"\n", ECHO, basefile);
  269.             printf ("%s '%s'\n", CHDIR, basefile);
  270.             }
  271.         else /* type == 'f' */
  272.             {
  273.             if (Verbose == true)
  274.                 printf ("%s: extracting \"'%s'\" '(%d character%s)'\n",
  275.                     ECHO, basefile, FSIZE, FSIZE > 1 ? "s" : "");
  276.             if (Silent == false) /* this solution by G|ran Uddeborg */
  277.                 {
  278.                 printf ("if %s -f '%s'\n", TEST, basefile);
  279.                 puts ("then");
  280.                 printf ("    %s: will not over-write existing file \"'%s'\"\n",
  281.                     ECHO, basefile);
  282.                 puts ("else");
  283.                 }
  284.             if (archive (file, basefile)) exit (-1);
  285.             }
  286.         }
  287.     else /* pos == 1, after the file is archived */
  288.         {
  289.         if (type == 'd')
  290.             {
  291.             if (Verbose == true)
  292.                 printf ("%s: done with directory \"'%s'\"\n", ECHO, basefile);
  293.             printf ("%s ..\n", CHDIR);
  294.             }
  295.         else /* type == 'f' (plain file) */
  296.             {
  297.             if (Count == true)
  298.                 {
  299.                 printf ("if %s %d -ne \"`%s '%s'`\"\n",
  300.                     TEST, FSIZE, WC_C, basefile);
  301.                 puts ("then");
  302.                 printf ("    %s: error transmitting \"'%s'\" ", ECHO, basefile);
  303.                 printf ("'(should have been %d character%s)'\n",
  304.                     FSIZE, FSIZE > 1 ? "s" : "");
  305.                 puts ("fi");
  306.                 }
  307.             if (access (file, 1) == 0) /* executable -> chmod +x */
  308.                 printf ("%s '%s'\n", CHMOD, basefile);
  309.             if (Silent == false)
  310.                 {
  311.                 puts ("fi # end of overwriting check");
  312.                 }
  313.             }
  314.         }
  315.     }
  316.